/*
 * OREKIT-X
 * Copyright 2002-2008 CS Communication & Systemes
 * 
 * Parts of this software package have been licensed to CS
 * Communication & Systemes (CS) under one or more contributor license
 * agreements.  See the NOTICE file distributed with this work for
 * additional information.
 *  
 * This is an experimental copy of OREKIT from www.orekit.org.
 * Please use the original OREKIT from orekit.org for normal work
 * unrelated to this research project.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you
 * may not use this file except in compliance with the License.  You
 * may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
 * implied.  See the License for the specific language governing
 * permissions and limitations under the License.
 */
package ore.time;

/**
 * A sorted set of {@link TimeStamped} for OREKIT.  This class
 * includes an efficient "brackets" operation to replace the
 * "head.last; tail.first" idiom.
 * 
 * @author John Pritchard
 */
public class ChronologicalSet<T extends TimeStamped>
    extends Object
    implements java.util.Set<T>,
               java.util.Comparator<T>
{
    /**
     * The "previous" field compares "strictly less than", while the
     * "next" field compares "greater than or equal to".
     */
    public final static class Brackets<T>
        extends Object
    {
        public final T previous, next;

        public Brackets(T p, T n){
            super();
            this.previous = p;
            this.next = n;
        }
        public Brackets(){
            super();
            this.previous = null;
            this.next = null;
        }

        public boolean isEmpty(){
            return (null == this.previous && null == this.next);
        }
        public boolean isNotEmpty(){
            return (null != this.previous || null != this.next);
        }
    }


    /**
     * Sorted set
     */
    private volatile Object[] sset;


    public ChronologicalSet(){
        super();
    }


    public final int size(){
        Object[] sset = this.sset;
        if (null == sset)
            return 0;
        else
            return sset.length;
    }
    public final boolean isEmpty(){
        return (null == this.sset);
    }
    public final boolean isNotEmpty(){
        return (null != this.sset);
    }
    public final boolean contains(Object item){
        if (item instanceof TimeStamped)
            return this.contains( (TimeStamped)item);
        else
            return false;
    }
    public final boolean contains(TimeStamped item){
        return (-1 != this.indexOf(item));
    }
    public final int indexOf(TimeStamped item){
        Object[] sset = this.sset;
        if (null != sset){
            switch (((T)sset[0]).compareTo(item)){
            case -1:
                for (int cc = 1, count = sset.length; cc < count; cc++){
                    switch (((T)sset[cc]).compareTo(item)){
                    case -1:
                        break;
                    case 0:
                        return cc;
                    case 1:
                        return -1;
                    }
                }
                return -1;
            case 0:
                return 0;
            case 1:
                return -1;
            default:
                throw new IllegalStateException();
            }
        }
        else
            return -1;
    }
    public final T get(int idx){
        Object[] sset = this.sset;
        if (null != sset && -1 < idx && idx < sset.length)
            return (T)sset[idx];
        else
            throw new ArrayIndexOutOfBoundsException(String.valueOf(idx));
    }
    public final T first(){
        return this.get(0);
    }
    public final T last(){
        return this.get(this.size()-1);
    }
    public final java.util.Iterator<T> iterator(){
        return new Iterator<T>(this.sset);
    }
    public final Object[] toArray(){
        Object[] sset = this.sset;
        if (null != sset){
            int len = sset.length;
            Object[] copy = new Object[len];
            System.arraycopy(sset,0,copy,0,len);
            return copy;
        }
        else
            return EmptyArray;
    }
    public final <T> T[] toArray(T[] array){
        Object[] sset = this.sset;
        if (null != sset){
            int len = sset.length;
            if (len == array.length){
                System.arraycopy(sset,0,array,0,len);
                return array;
            }
            else if (len < array.length){
                System.arraycopy(sset,0,array,0,len);
                array[len] = null;
                return array;
            }
            else {
                array = (T[])java.lang.reflect.Array.newInstance(array.getClass().getComponentType(),len);
                System.arraycopy(sset,0,array,0,len);
                return array;
            }
        }
        else {
            if (0 < array.length)
                array[0] = null;
            return array;
        }
    }
    public final boolean add(T item){
        if (null != item){
            int index = this.indexOf(item);
            if (-1 != index){
                if (this.sset[index].equals(item))
                    return false;
                else {
                    this.sset[index] = item;
                    return true;
                }
            }
            else {
                Object[] sset = this.sset;
                if (null == sset)
                    this.sset = new Object[]{item};
                else {
                    int len = sset.length;
                    Object[] copier = new Object[len+1];
                    System.arraycopy(sset,0,copier,1,len);
                    copier[0] = item;
                    java.util.Arrays.sort(copier);
                    this.sset = copier;
                }
                return true;
            }
        }
        else
            return false;
    }
    public final boolean remove(Object item){
        throw new UnsupportedOperationException();
    }
    public final boolean containsAll(java.util.Collection<?> col){
        for (Object item: col){
            if (!this.contains(item))
                return false;
        }
        return true;
    }
    public final void addAll(java.lang.Iterable<T> it){
        for (T item: it){
            this.add(item);
        }
    }
    public final boolean addAll(java.util.Collection<? extends T> col){
        boolean changed = false;
        /*
         * Maintains uniqueness
         */
        for (T item: col){
            if (this.add(item))
                changed = true;
        }
        return changed;
    }
    public final boolean removeAll(java.util.Collection<?> col){
        throw new UnsupportedOperationException();
    }
    public final boolean retainAll(java.util.Collection<?> col){
        throw new UnsupportedOperationException();
    }
    public final void clear(){
        this.sset = null;
    }
    public final Brackets<T> brackets(AbsoluteDate date){
        Object[] sset = this.sset;
        if (null != sset){
            final int sign = ((T)sset[0]).compareTo(date);
            for (int cc = 1, count = sset.length; cc < count; cc++){
                if (sign != ((T)sset[cc]).compareTo(date))
                    return new Brackets((T)sset[cc-1],(T)sset[cc]);
            }
            if (0 > sign)
                return new Brackets<T>((T)sset[0],null);
            else
                return new Brackets<T>(null,(T)sset[0]);
        }
        else
            return new Brackets<T>();
    }
    public final int compare(T a, T b){
        return a.getDate().compareTo(b.getDate());
    }

    /**
     * Iterator for chronological set.
     */
    private final static class Iterator<T>
        extends Object
        implements java.util.Iterator<T>
    {
        private final Object[] list;
        private final int count;
        private int index;

        Iterator(Object[] list){
            super();
            if (null == list){
                this.list = null;
                this.count = 0;
            }
            else {
                this.list = list;
                this.count = list.length;
            }
        }

        public boolean hasNext(){
            return (this.index < this.count);
        }
        public T next(){
            int index = this.index++;
            if (index < this.count)
                return (T)this.list[index];
            else
                throw new java.util.NoSuchElementException();
        }
        public void remove(){
            throw new UnsupportedOperationException();
        }
    }
    private final static Object[] EmptyArray = new Object[0];
}
